<template>
    <div class="aaaaaaaa">
        <div>父组件</div>
        <!-- <div class="a">aaaaaaaaaaaa</div> -->
        <button @click="changeProvideValue">provide / inject</button>&nbsp;
        <button @click="changeRefsValue">ref / $refs</button>&nbsp;
        <button @click="changeChildren">$parent / $children</button>&nbsp;
        <!-- .sync修饰符：可以理解为在不设置自定义事件的情况下改变父组件的值 -->
        <Son :id="id" :syncNum.sync="syncNum" @my-event="emitFn" ref="son" v-if="isShowSon"></Son>
        <Son :id="id" :syncNum.sync="syncNum" @my-event="emitFn" ref="son" v-if="isShow"></Son>
        <button @click="isShow =!isShow">v-if是否显示子组件</button>


<!-- 动态添加样式(class)：用于添加多种属性组成的样式==================================================================== -->
    <!-- 三元运算符：添加两种样式中的一种 -->
        <div :class="number ? 'activeRed' : 'activeGreen'">(class) 三元运算符</div>

    <!-- 对象的形式：是否添加某一种，添加多种 -->
        <div :class="{activeRed: number, activeGreen:!number}">(class) 对象的形式1</div>
        <!-- 和普通的 class一起用。同时其实这个对象也可以定义为一个数据对象，然后作为class的表达式值 -->
        <!-- <div class="static" :class="{ active: isActive, 'text-danger': hasError }">(class) 对象的形式2</div> -->
        <!-- 同时其实这个对象也可以定义为一个数据对象，然后作为class的表达式值 -->
        <!-- <div :class="classObject">(class) 对象的形式3</div> -->

    <!-- 数组的形式 -->
        <div :class="[activeClass,errorClass]">(class) 数组的形式1</div>
        <div :class="this.style">(class) 数组的形式2</div>
    
    <!-- 使用全局样式 -->
        <div class="global">使用了公共的样式</div>

    <!-- class用在组件上：这些 class 将被添加到该组件的根元素上面。这个元素上已经存在的 class 不会被覆盖。 -->
        <!-- 当 isActive 的值为true时，子组件的根元素样式为  class="son baz boo active"-->
        <!-- <Son class="baz boo" :class="{ active: isActive }"></Son> -->


<!-- 动态添加样式(style)：用于添加某种属性样式========================================================================== -->
    <!-- 三元运算符 -->
        <div :style="number ? 'background: skyblue' : 'background: pink'">(style) 三元运算符</div>

    <!-- 对象的形式 -->
        <div :style="{background: number ? 'skyblue' : 'pink'}">(style) 对象的形式1</div>
        <!-- <div :style="{ color: activeColor, fontSize: fontSize + 'px' }">(style)对象的形式2</div> -->

    <!-- 数组的形式：将多个样式对象应用到同一个元素上： -->
        <!-- <div :style="[baseStyles, overridingStyles]">(style) 数组的形式</div> -->


    </div>
    
</template>

<script>
import Son from '@/views/Son.vue';
import Vue from 'vue'
    export default {
        components:{Son},
        data(){
            return{
                obj: {          //测试delete和Vue.delete的区别
                    a:"a",
                    b:"b",
                },
                deleteArr: [1,2,3],
                aaaaa : 123456,

                isShow:true,
                isShowSon:false,
                num: 1,         //$emit
                syncNum: 2,     //$emit的另一种写法
                id: 10,         //props
                provideValue: "provideValue",
                parentChildrenValue: "新的ParentChildrenValue",
                refValue: "新的RefValue",


                number: false,  //动态添加样式！！！
                // classObject: {  //把class的值写成数据
                //     active: true,
                //     'text-danger': false
                // },
                activeClass: 'activeRed',   //数组形式的动态添加样式
                errorClass: 'activeGreen',
                style:['activeRed', 'activeGreen'],


                // 常见数据的结构总类：
                a:1, b:"1", c:true,                 //一层
                d:[1,2,3], e:{a:1,b:2},             //两层
                f:[1,{a:1},[1,2]], h:{a:1,b:{c:1,d:2},c:[1,2,3]}, 
                g:[{b:[1,2]},{},{}], 
                g:[{b:[{a:1,b:2}]},{},{}], 
            }
        },

        /**
         * 不同的父链组件provide注入相同名称的数据，则采用就近原则拿到距离该组件层级最近的父链组件注入的值。
         * 如果data中存在该变量，inject又注入了相同的变量，在页面中会显示data中的值。
         */
        // provide: {
        //     provideValue: this.provideValue
        //     provideValue: ()=>this.provideValue      //这样写可以做到响应式
        // },
        provide(){
            return {
                // provideValue: this.provideValue,    //数据不是响应式的
                provideValue: ()=>this.provideValue      //这样写可以做到响应式
            }
        },

        methods:{
            emitFn(value){
                this.num = this.num + value
                console.log(this.num);

            },
            changeProvideValue(){
                this.provideValue = "newProvideValue"   //验证依赖注入是不是响应式的
            },
            changeRefsValue(){
                console.log(this.$refs, this.$refs.son[0]);
                this.$refs.son.refValue = this.refValue
                // this.refValue = "ref不具有响应式"    //不具有响应式
            },
            changeChildren(){
                // console.log(this.$children);
                // this.isShowSon = true
                this.$children[0].parentChildrenValue = this.parentChildrenValue
                // this.parentChildrenValue = "sdfghjkjhgfds"   // 可以看到虽然父组件中的这个数据发生了变化，但是不具有响应式

                // this.$children[0].id = "dfghjddfdg"  //这样子直接修改父组件传给子组件的数据会警告
            },
        },

  
// ==============================================================================================================================
// ==============================================================================================================================
// 生命周期：

        beforeCreate(){},

        created(){

        // ———————————————————————————————— 实例方法(数据相关)：$watch, $set, $delete ——————————————————————————————————————————
        // ———————————————————————————————— 实例方法(数据相关)：$watch, $set, $delete ——————————————————————————————————————————
        
            // var a = this.$watch(this.provideValue,(newVal, oldVal)=>{   //a是一个取消观察函数，用来停止触发回调
            //     console.log(newVal, oldVal);
            // })
            // var b = this.$watch(()=>{       //同时监听多个数据, 通过第一个函数的参数获取到要监听的数据
            //     var c = this.provideValue
            //     var d = this.syncNum
            //     return {c,d}
            // },(newVal, oldVal)=>{   
            //     console.log(newVal, oldVal);
            // })

            // this.$set(this.arr, 2, "c")
            // console.log(this.arr);
            // this.$set(this.obj, "c", 10)
            // console.log(this.obj);
            
            // this.$delete(this.obj, "b")
            // console.log(this.obj);
            // delete this.obj.b           //可以删除，但是视图不会发生变化
            // delete this.deleteArr[1]    //被操作的项变成 empty 或 undefined，数组的长度不会变化
            // console.log(this.obj, this.deleteArr);  

        // ——————————————————————————————— 实例方法(事件相关)：$emit, $on, $once, $off ————————————————————————————————————————————
        // ——————————————————————————————— 实例方法(事件相关)：$emit, $on, $once, $off ————————————————————————————————————————————
        

            // this.$emit("event", 10)
            // this.$on("event", ()=>{})

            // 监听一个事件/beforeDestroy生命周期的触发，只触发一次该函数。一旦触发，监听器就会被移除。页面销毁前执行该函数
            // 应用场景：清除定时器；取消事件的监听
            // this.$once(event, callback)

            /** 移除自定义事件监听器:
             * - 如果没有提供参数，则移除实例的所有事件的监听器；
             * - 如果只提供了事件，则移除该事件所有的监听器；
             * - 如果同时提供了事件与回调，则只移除这个回调的监听器。
             */
            // this.$off([event], [callback])    

        // ————————————————————— 实例方法(生命周期相关): $nextTick, $mount, $forceUpdate, $destrory ——————————————————————————————
        // ————————————————————— 实例方法(生命周期相关): $nextTick, $mount, $forceUpdate, $destrory ——————————————————————————————

            // this.$nextTick(()=>{})

            // this.$mount(Element | string)
            
            //强制 Vue 实例重新渲染。需要注意的是它仅仅影响实例本身和插入插槽内容的子组件，而不是所有子组件。
            //$forceUpdate 方法会跳过所有的组件优化和缓存机制，
            // this.$forceUpdate()  

            // 完全销毁一个实例。清理它与其它实例的连接，解绑它的全部指令及事件监听器。触发 beforeDestroy 和 destroyed 的钩子。
            // this.$destroy()

        },

        beforeMount(){
            // console.log(this.$vnode);       //获取当前自定义组件在父组件中的vnode的虚拟节点   能拿到
            // console.log(this._vnode);       //获取当前自定义组件的虚拟节点    null
            
            // console.log(this.$refs);        //因为$refs是在组件渲染完成后才填充，这涉及到父子组件间的生命周期顺序，
            // console.log(this.$el);          //这个也还没拿得到      undefined
        },

        mounted(){
            // 实例属性：实例属性：实例属性：实例属性：实例属性：实例属性：实例属性：实例属性：
            // console.log(this.$data);
            // console.log(this.$props);
            // console.log(this.$attrs);
            // console.log(this.$listeners);
            // console.log(this.$parent);
            // console.log(this.$children);
            // console.log(this.$refs);         //只有在 mounted 这个生命周期中才能获取的到，
            // console.log(this.$root);         //获取到跟组件（app组件）
            // console.log(this.$el);           //只有在 mounted 这个生命周期中才能获取的到，否则为undefined
            // console.log(this.$slots);
            // console.log(this.$scopedSlots);
            // console.log(this.$store);
            // console.log(this.$router);
            // console.log(this.$route);
            // console.log(this.$options);      //用于当前 Vue 实例的初始化选项。需要在选项中包含自定义 property 时会有用处
            // console.log(this.$isServer);     //当前 Vue 实例是否运行于服务器
            // console.log(this.$ssrContext);
            // console.log(this.$vnode);        //获取当前自定义组件在父组件中的vnode的虚拟节点   能拿到
            // console.log(this._vnode);        //获取当前自定义组件的虚拟节点        能拿到

            // console.log("this", this);
            // console.log("Vue.prototype", Vue.prototype);
            // console.log("this.__proto__", this.__proto__);
        },
        beforeUpdate(){},
        updated(){},
        activated(){},
        deactivated(){},
        beforeDestroy(){},
        destroyed(){},
        errorCaptured(err, vm, info){   // 此钩子会收到三个参数：错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。
            // console.log(err, vm, info);
        },
// =============================================================================================================================
// =============================================================================================================================
// // 数据：

        // data(){return {}},
        // props:[],
        // propsData:[],
        // watch:{},
        // computed:{},
        // methods:{},
// =============================================================================================================================
// =============================================================================================================================
// // 资源：

        // directives:{},
        // filters:{},
        // components:{},
// =============================================================================================================================
// =============================================================================================================================
// // 组合：

        // parent,                  // 指定已创建的实例之父实例，在两者之间建立父子关系。
        // mixins: [{}],
        // extends: {} | Function,  // 声明扩展另一个组件 (可以是一个简单的选项对象或构造函数)，而无需使用 Vue.extend。主要是为了便于扩展单文件组件。
        // provide:{} | ()=> Object,
        // inject:[] | {},
// =============================================================================================================================
// =============================================================================================================================
// // DOM：

        // el: "" | Element,
        // template: "",
        // ————————————————————————————————————————————————————————————————————————————————————————————————

        // render: (createElement: () => VNode) => VNode,
        // render(createElement){
            /** createElement 函数的参数：
             * @params (String \| Object \| Function) Element 要创建的元素
             * @params (Array) Attrs 元素的属性
             * @params (String | Array) ElementContent 元素内容节点/文本内容
             */
        //     return  createElement( 

        //     )
        // },

        // ————————————————————————————————————————————————————————————————————————————————————————————————
        // renderError: (createElement: () => VNode, error: Error) => VNode,

// =============================================================================================================================
// =============================================================================================================================
// //其他：

        // name: "father",
        // delimiters: ["{{", "}}"],
        // functional: true | false,
        // model: { prop?: string, event?: string },
        // inheritAttrs: true | false,
        // comments: false | true,//为 `true` 时，将会保留且渲染模板中的 HTML 注释。默认行为是舍弃它们。只在完整构建版本中的浏览器内编译时可用。
    }
</script>

<style>
/* 使用 scoped 后，父组件的样式将不会渗透到子组件中。
不过，子组件的根节点还是会同时被父组件的作用域样式和子组件的作用域样式影响。
就是说加给父组件的data-v-hash的值也会加给子组件的根元素   子组件的其他元素没有
这样设计是为了让父组件可以从布局的角度出发，调整其子组件根元素的样式。 */

/* 如果父子组件都没有加 scoped ，并且父子组件都含有相同名称的选择器的话。父组件的样式会覆盖子组件的
   只要父子组件其中一个加了 scoped，那么各自组件的样式就只能影响到自己 。
   如果父子组件都加了 scoped ， 那么子组件的根元素就会有两个 data-v-hash 格式的属性. */

/* 如果加了 scoped ，但是我们想在父组件中修改掉 子组件中的 非根元素的 元素的样式，  (子组件根元素的样式直接改就行)
   就要使用深度选择器 >>>, /deep/(vue2), ::v-deep(vue3), :deep(子组件中的元素的选择器名) 来修改。 这在修改 第三方组件库的样式 时很有用 */

/* 注意：虽然深度选择器很有用，但过度使用它可能会导致样式难以维护和理解，因为它打破了组件的封装性。
   在可能的情况下，最好通过 props 和 events 与子组件通信，而不是直接修改其样式。 */


    /* .a {
        color: v-bind(color);
    } */
    .activeRed{
        background-color: red;
    }
    .activeGreen{
        color: green;
    }
</style>
<!-- 同一个组件中同时包含作用域样式和非作用域样式： -->